package testApplets;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

import marchingCubes.CAADMCube;
import processing.core.PApplet;
import processing.opengl.*;

public class VoxelShapeAppletSpirals extends PApplet {
	//dimensions of voxelgrid
	int nX =200;
	int nY = 200;
	int nZ = 3;
	//200x100x50

	// precalculated values
	int nYZ=nY*nZ;
	int nXYZ=nY*nZ*nX;

	// declaration and construction of the voxelgrid
	float[] voxels = new float[nXYZ];
	int[]colorTypes=new int[nXYZ];
	int[]colorValues=new int[nXYZ];
	
	
	float iso=0.5f;

	CAADMCube mCube=new CAADMCube();
	boolean doExport;

	public void setup() {
	  size(1000, 700, OPENGL);
	  setValues();
	  for (int i=0;i<nXYZ;i++) {
		    colorTypes[i]=(int)random(4);
		  }
	}
	public void rpc() {
		  for (int x=0;x<nX;x++) {
		    for (int y=0;y<nY;y++) {
		      for (int z=0;z<nZ;z++) {
		        int index=getIndex(x, y, z);
		        int rndDir=(int)random(6);
		        int nbIndex=0;

		        if (rndDir==0&&index+1<nXYZ)nbIndex=index+1;
		        if (rndDir==1&&index-1>0)nbIndex=index-1;
		        if (rndDir==2&&index+nZ<nXYZ)nbIndex=index+nZ;
		        if (rndDir==3&&index-nZ>0)nbIndex=index-nZ;
		        if (rndDir==4&&index+nYZ<nXYZ)nbIndex=index+nYZ;
		        if (rndDir==5&&index-nYZ>0)nbIndex=index-nYZ;
		        if (colorTypes[index]==0) {
		          if (colorValues[nbIndex]<9) {
		            colorTypes[index]=colorTypes[nbIndex];
		            colorValues[index]=colorValues[nbIndex]+1;
		          }
		        }
		        else if (colorTypes[index]!=colorTypes[nbIndex]) {
		          int i1=index;
		          int i2=nbIndex;
		          if (colorTypes[i1]>colorTypes[i2]) {
		            i1=nbIndex;
		            i2=index;
		          }
		          if (colorTypes[i1]==1&&colorTypes[i2]==2) {
		            feed(i1, i2);
		          }
		          if (colorTypes[i1]==2&&colorTypes[i2]==3) {
		            feed(i1, i2);
		          }
		          if (colorTypes[i1]==1&&colorTypes[i2]==3) {
		            feed(i2, i1);
		          }
		        }
		      }
		    }
		  }
		}
		public void feed(int i1, int i2) {
		  colorValues[i1]=min(colorValues[i1]+1, 10);
		  colorValues[i2]=max(colorValues[i2]-1, 0);
		  if (colorValues[i1]==10)colorTypes[i1]=0;// killed white
		}
	
	public void displayRPC() {
		int index = 0;
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					int cType = colorTypes[index];
					// if (cType==0)fill(255);
					if (cType == 1) {
						/*
						 * fill(colorValues[index]*25, 0, 0); pushMatrix();
						 * translate(x, y, z); box(1); popMatrix();
						 */
						voxels[index] = colorValues[index];
					} else {
						voxels[index] = 11;
					}
					// if (cType==2)fill(0, 255, 0);
					// if (cType==3)fill(0, 0, 255);
					index++;
				}
			}
		}
		calculateBorders(0);
	}

	public void calculateBorders(int value){
		   for (int x=0;x<nX;x++) {
		    for (int y=0;y<nY;y++) {
		      for (int z=0;z<nZ;z++) {
		        if (x==0||x==nX-1||y==0||y==nY-1||z==0||z==nZ-1){
		          int i=getIndex(x, y, z);
		          voxels[i]=value;
		        }
		      }
		    }
		  }
		}
	public void setValues() {
	  for (int x=0;x<nX;x++) {
	    for (int y=0;y<nY;y++) {
	      for (int z=0;z<nZ;z++) {
	        int i=getIndex(x, y, z);
	        float value=sin(x*PI*5f/nX);
	        
	        value+=pow(sin(2f*y/nY  + 1),mouseY*20f/height) * 2f;
	        value+=abs(sin((20f)*y*PI/nY+1))*0.2f;
	        value+=sin(5*x*PI/nX+2.5f);
	        value+=sin(2*z*PI/nZ+5);
	        value+=cos(20*(x+z)*PI/(nZ+nX)+5);
	        value+=sin(sqrt((x-5)*(x-5)+(y-20)*(y-20)+(z-13)*(z-13))/10f);
	        voxels[i]=value;
	        
	      }
	    }
	  }
	  
	  normalize(voxels);
	  // now we modify the isovalues again
	  for (int i=0;i<voxels.length;i++){
	   //voxels[i]= abs(sin(32f*voxels[i]  + 1));
	  }
	 
	  for (int x=0;x<nX;x++) {
		    for (int y=0;y<nY;y++) {
		      for (int z=0;z<nZ;z++) {
		    	  int i=getIndex(x, y, z);
		    	  voxels[i]-=sin(2*z*PI/nZ+5);
		      }
		    }
	  }
	  normalize(voxels);
	  // borders
	  for (int x=0;x<nX;x++) {
	    for (int y=0;y<nY;y++) {
	      for (int z=0;z<nZ;z++) {
	        if (x==0||x==nX-1||y==0||y==nY-1||z==0||z==nZ-1){
	          int i=getIndex(x, y, z);
	          voxels[i]=0;
	        }
	      }
	    }
	  }
	 
	  
	}

	public void draw() {
	 //setValues();
	 for (int i=0;i<100;i++) {
		   rpc();
	}
	 
	 displayRPC();
	 normalize(voxels);
	 //iso=8.5f;
	 mCube.marchingCubesMinim(nX,nY,nZ,voxels,iso,1,0,0,0,null);
	 if (doExport) {
	    doExport = false;
	    mCube.saveObj(this.selectOutput());
	  }
	  
	  // setup of scene
	  background(0);
	  camera(0, 0,nX, 0, 0, 0, 0, 1, 0);
	  lights();
	  //pointLight(200, 210, 200, 0, 0, 0);
	  noStroke();
	 rotateY(frameCount/10f);
	  translate(-nX*0.5f, -nY*0.5f, -nZ*0.5f);
	  iso=mouseX*1f/width;
	  println(iso);
	  /*iso=max(0.1f,iso);
	  iso=min(0.9f,iso);*/
	  
	  
	  beginShape(TRIANGLES);
	  if (mCube.triangles.length>0&&mCube.xc.length>0){
	    for (int i=0;i<mCube.triangles.length;i++){
	      int vI=mCube.triangles[i];
	      vertex(mCube.xc[vI],mCube.yc[vI],mCube.zc[vI]);
	    }
	  }
	  endShape();
	  //drawAsQuads();
	}


	public void normalize(float[] values) {
	  float maxV = Float.NEGATIVE_INFINITY;
	  float minV = Float.POSITIVE_INFINITY;
	  for (int i = 0; i < values.length; i++) {
	    float v = values[i];
	    maxV = max(maxV, v);
	    minV = min(minV, v);
	  }
	  float dX = maxV - minV;
	  for (int i = 0; i < values.length; i++) {
	    float v = values[i];
	    values[i] = (v - minV) / dX;
	  }
	}

	// gui
	public void keyPressed() {
	  if (key == 'e')
	    doExport = true;
	}

	public int getIndex(int x, int y, int z) {
		  return x*nY*nZ+y*nZ+z;
		}
		public int getX(int i) {
		  return i/(nY*nZ);
		}
		public int getY(int i) {
		  return (i/nZ)%nY;
		}
		public int getZ(int i) {
		  return i%nZ;
		}
	
		public void drawAsQuads(){
			  // looping to draw the voxels
			  beginShape(QUADS);
			  for (int x=0;x<nX-1;x++) {
			    for (int y=0;y<nY-1;y++) {
			      for (int z=0;z<nZ-1;z++) {
			        int i=getIndex(x, y, z);
			        //  draw a face between voxel and z neighbour
			        if (voxels[i]<iso!=voxels[i+1]<iso) {
			          drawZQuad(x, y, z+0.5f);
			        }
			        //  draw a face between voxel and y neighbour
			        if (voxels[i]<iso!=voxels[i+nZ]<iso) {
			          drawYQuad(x, y+0.5f, z);
			        }
			        //  draw a face between voxel and x neighbour
			        if (voxels[i]<iso!=voxels[i+nYZ]<iso) {
			          float xM=x+0.5f;
			          drawXQuad(x+0.5f, y, z);
			        }
			      }
			    }
			  }
			  endShape();
			}

			void drawZQuad(float x,float y,float z){
			  vertex(x-0.5f,y-0.5f,z);
			  vertex(x-0.5f,y+0.5f,z);
			  vertex(x+0.5f,y+0.5f,z);
			  vertex(x+0.5f,y-0.5f,z);
			}
			void drawYQuad(float x,float y,float z){
			  vertex(x-0.5f,y,z-0.5f);
			  vertex(x-0.5f,y,z+0.5f);
			  vertex(x+0.5f,y,z+0.5f);
			  vertex(x+0.5f,y,z-0.5f);
			}
			void drawXQuad(float x,float y,float z){
			  vertex(x,y-0.5f,z-0.5f);
			  vertex(x,y-0.5f,z+0.5f);
			  vertex(x,y+0.5f,z+0.5f);
			  vertex(x,y+0.5f,z-0.5f);
			}
}
